#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

/*
Finish Work 1 
Finish Work 2 
Finish Work 3 
Finish Work 4 
Finish Work 5 
......
Finish Work 100
 *
 */

#define THREAD_NUM 10
//生产者：任务
typedef struct Task{
	//任务的具体执行逻辑 （函数指针）
	void *(*func)(void * arg);
	//函数参数
	void * arg;
	//下一个任务
	struct Task * next;
}Task;

//线程池
typedef struct ThreadPool{
	//互斥锁：访问临界资源（任务队列）时使用
	pthread_mutex_t mutex;
	//条件变量：访问临界资源（任务队列）时使用
	pthread_cond_t cond;
	//线程池中正在工作的线程数
	int busywork;
	//线程数组
	pthread_t tid[THREAD_NUM];
	//任务队列的头节点
	Task * head;
}ThreadPool;

//全局变量：线程池
ThreadPool * pool;

/*
   每个线程处理的具体逻辑
   */
void * dealTask(void * arg){
	Task * ptask;
	while(1){

		//1. 因为要处理临界资源，所以先加锁与条件变量
		pthread_mutex_lock(&pool->mutex);
		pthread_cond_wait(&pool->cond, &pool->mutex);

		//2.1 消费一个资源
		ptask = pool->head;
		pool->head = pool->head->next;

		//2.2 具体逻辑
		ptask->func(ptask->arg);
		free(ptask);
		pool->busywork--;

		pthread_mutex_unlock(&pool->mutex);
	}
}


/*
   初始化线程池 （等待的线程类似于消费者）
   */
void initPool(){
	pool = malloc(sizeof(ThreadPool));
	//1. 动态初始化互斥锁、条件变量、正在工作的线程数、任务队列头节点
	pthread_mutex_init(&pool->mutex, NULL);
	pthread_cond_init(&pool->cond, NULL);
	pool->busywork = 0;
	pool->head = NULL;
	//2. 创建线程 0-9共10个线程
	for(int i = 0; i < THREAD_NUM; i++){
		pthread_create(&pool->tid[i], NULL, dealTask, NULL);
	}

}

/*
   具体执行逻辑
   */
void * realWork(void * arg){
	printf("Finish Work %d \n", (int)arg);
}

/*
   线程池处理任务 类似于生产者
   */
void poolAddTask(int num){

	//线程池中线程数量有限（10个线程），一次来100个任务，如果不加等待的话，会出现信号量丢失的问题，加入如下代码解决
	//这也是为什么加 busywork 变量的原因   当所有线程都在工作时，生产者休眠，不让其继续生产任务。
	//这里加锁的目的暂时还不太清楚 TODO   目前猜测可能是因为访问的 busywork 是临界资源，不加可能有其他不可知的问题
	//这里经过实践 不加锁也可以满足要求。
	pthread_mutex_lock(&pool->mutex);
	while(pool->busywork >= THREAD_NUM){
		pthread_mutex_unlock(&pool->mutex);
		usleep(10000);
		pthread_mutex_lock(&pool->mutex);
	}
	pthread_mutex_unlock(&pool->mutex);



	//新建任务
	Task * ptask;
	ptask = malloc(sizeof(Task));
	ptask->arg =(void *) num;
	//函数指针
	ptask->func = realWork;
	ptask->next = NULL;

	//处理临界资源，加上互斥锁与条件变量
	pthread_mutex_lock(&pool->mutex);
	if(pool->head == NULL){
		pool->head = ptask;
	}else {
		Task * temp = pool->head;
		while(temp->next != NULL){
			temp = temp->next;
		}
		temp->next = ptask;
	}
	pool->busywork++;
	//发送信号，唤起休眠线程
	pthread_cond_signal(&pool->cond);
	pthread_mutex_unlock(&pool->mutex);

}
/*
   释放内存
   */
void freeMem(){
	Task * ptask;
	//释放任务队列里的每一个节点。这里应该不需要，因为消耗的时候，已经将节点释放了
	while(pool->head != NULL){
		ptask = pool->head;
		pool->head = pool->head->next;
		free(ptask);
	}
	//释放锁
	pthread_mutex_destroy(&pool->mutex);
	pthread_cond_destroy(&pool->cond);
	//释放线程池
	free(pool);
	//线程的回收等到程序结束后自动回收
}

int main(){
	//1. 初始化线程池  (等待的线程类似于消费者)
	//这一行执行完，过一会儿后，线程都在 pthread_cond_wait 处阻塞等待
	initPool();
	sleep(5);
	//2. 处理100个任务 （新来的任务类似于生产者）
	for(int i = 1; i <= 100; i++){
		poolAddTask(i);
	}
	sleep(5);
	//3. 回收内存
	freeMem();

}
